﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace Remote_Scripts_Client
{
    public partial class MainWindow : Form
    {
        public static MainWindow Window;

        public NetworkManager NetworkManager;

        private Dictionary<string, Script> scripts;
        private string currentScriptName;
        private TextBox[] parameterTextBoxes;
        private Label[] parameterLabels;

        public MainWindow()
        {
            Window = this;
            InitializeComponent();

            SetStatus("Łączenie z serwerem...");
            this.NetworkManager = new NetworkManager();

            this.scriptsView.NodeMouseClick += ScriptsListOnNodeMouseClick;
        }

        /// <summary>
        /// Funkcja ładująca skrypty z podanej listy.
        /// </summary>
        /// <param name="s">Słownik skryptów.</param>
        public void LoadScripts(Dictionary<string, Script> s)
        {
            this.scripts = s;
            this.refreshScriptView();
        }

        /// <summary>
        /// Funkcja ustawiająca status na pasku statusu.
        /// </summary>
        /// <param name="status"></param>
        public static void SetStatus(string status)
        {
            MainWindow.Window.statusLabel.Text = status;
        }

        /// <summary>
        /// Funkcja dodająca listę skryptów do podanej kategorii w widoku skryptów.
        /// </summary>
        /// <param name="category">Kategoria, do której mają być dodane skrypty</param>
        /// <param name="scripts">Lista skryptów do dodania.</param>
        private void refreshScriptView()
        {
            if (this.scriptsView.InvokeRequired == true)
            {
                this.Invoke((MethodInvoker) refreshScriptView);
                return;
            }

            this.scriptsView.BeginUpdate();
            this.clearScripts();

            foreach (var script in this.scripts)
            {
                this.scriptsView.Nodes.Add(script.Value.Name);
            }

            this.scriptsView.EndUpdate();
        }

        /// <summary>
        /// Funkcja usuwa wszystkie kategorie z widoku
        /// </summary>
        private void clearScripts()
        {
            this.scriptsView.Nodes.Clear();
        }

        private void ScriptsListOnNodeMouseClick(object sender, TreeNodeMouseClickEventArgs treeView)
        {
            this.currentScriptName = treeView.Node.Text;

            refreshParameterTextBoxes(
                this.scripts[treeView.Node.Text].NumberOfParameters
                );
        }

        private void refreshParameterTextBoxes(int n)
        {
            clearParameterGroupBoxChildren();
            addDescription();
            addTextBoxesAndLabels(n);
        }

        private void addTextBoxesAndLabels(int n)
        {
            Point margin = new Point(15, 30);
            Size textBoxSize = new Size(70, 10);
            Point location = new Point(margin.X, margin.Y);
            Size labelSize = new Size(70, 15);
            const int distance = 18;


            this.parameterTextBoxes = new TextBox[n];
            this.parameterLabels = new Label[n];

            for (int i = 0; i < n; i++)
            {
                location.X += 80;

                if (i % 4 == 0)
                {
                    location.Y += 50;
                    location.X = margin.X;
                }

                parameterTextBoxes[i] = new TextBox()
                    {
                        Size = textBoxSize,
                        Location = location
                    };

                parameterLabels[i] = new Label()
                    {
                        Size = labelSize,
                        Location = new Point(location.X, location.Y - distance),
                        Text = this.scripts[this.currentScriptName].ParameterNames[i] + " (" + this.scripts[this.currentScriptName].ParameterTypes[i] + ")"
                    };
            }

            this.parameterGroupBox.SuspendLayout();

            for (int i = 0; i < n; i++)
            {
                this.parameterGroupBox.Controls.Add(parameterTextBoxes[i]);
                this.parameterGroupBox.Controls.Add(parameterLabels[i]);
            }

            this.parameterGroupBox.ResumeLayout(true);
        }

        private void addDescription()
        {
            this.scriptDescriptionLabel.Text = this.scripts[this.currentScriptName].Description;
        }

        private void clearParameterGroupBoxChildren()
        {
            if (parameterTextBoxes == null) return;

            for (int i = 0; i < this.parameterTextBoxes.Length; i++)
            {
                this.parameterTextBoxes[i].Dispose();
                this.parameterTextBoxes[i] = null;
                this.parameterLabels[i].Dispose();
                this.parameterLabels[i] = null;
            }

            this.parameterTextBoxes = null;
            this.parameterLabels = null;
        }

        private void opcjeToolStripMenuItem_Click(object sender, EventArgs e)
        {
            new OptionWindow().ShowDialog();
        }

        /// <summary>
        /// Event, który uruchamia się w momencie wyłączania aplikacji. Zwalnia potrzebne zasoby.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MainWindow_FormClosing(object sender, FormClosingEventArgs e)
        {
            this.NetworkManager.Disconnect();
        }

        private void execButton_Click(object sender, EventArgs e)
        {
            try
            {
                SetStatus("Trwa wykonywanie skryptu...");
                this.NetworkManager.ExecScript(this.currentScriptName, getParameters());
            }
            catch (ArgumentException aex)
            {
                MessageBox.Show("Podano błędne wartości!\n" + aex.Message);
            }
        }
        
        /// <summary>
        /// Pobiera wszystkie wartości parametrów.
        /// </summary>
        /// <returns>Listę parametrów</returns>
        private string getParameters()
        {
            var result = new StringBuilder();

            for (int i = 0; i < scripts[this.currentScriptName].NumberOfParameters; i++)
            {
                string value = this.parameterTextBoxes[i].Text;
                string type = this.scripts[this.currentScriptName].ParameterTypes[i];

                if (this.checkParameter(type, value) == false)
                {
                    throw new ArgumentException(value + " is not " + type);
                }

                if (type == "string")
                {
                    value = '"' + value + '"';
                }

                result.Append(value + ' ');
            }

            return result.ToString();
        }

        /// <summary>
        /// Funkcja sprawdzająca poprawnośc parametrów.
        /// </summary>
        /// <param name="type">Typ parametru.</param>
        /// <param name="value">Wartość parametru.</param>
        /// <returns>True jeśli wartość jest poprawna</returns>
        private bool checkParameter(string type, string value)
        {
            if (type == "string")
            {
                return true;
            }

            if (type == "int")
            {
                int i = 0;
                return int.TryParse(value, out i);
            }

            if (type == "float")
            {
                float f = 0;
                return float.TryParse(value, out f);
            }

            return false;
        }
    }
}
